import anime from 'animejs'
import { edgeConfig } from '../config/edgeConfig'
import { sizeConfig } from '../config/sizeConfig'
import { IDataValue, IModuleWithState } from '../types'
import { createDataValue, getCenterPoint, toBinaryStr } from '../utils'
import { conSignal } from './conSignal'
import { ir } from './ir'
import { sm } from './sm'

interface IDecoderState {
    /**
     * 当前匹配到的指令
     */
    ins: string
}

interface IDecoder extends IModuleWithState<IDecoderState> {
    dataItem: IDataValue<string> | null
}

/**
 * 指令表
 */
const instructions = {
    __EMPTY__: '(EMPTY)',
    0b0100: 'MOVA',
    0b0101: 'MOVB',
    0b0110: 'MOVC',
    0b0111: 'MOVD',
    0b1000: 'ADD',
    0b1001: 'SUB',
    0b1010: 'JMP',
    0b1011: 'JG',
    0b1100: 'IN',
    0b1101: 'OUT',
    0b1110: 'MOVI',
    0b1111: 'HALT',
} as const

type InsToCodeType<
    K extends keyof typeof instructions = keyof typeof instructions
> = (typeof instructions)[K]

const insToCode: InsToCodeType = Object.keys(instructions).reduce(
    (acc: InsToCodeType, cur) => {
        // @ts-ignore
        acc[instructions[cur]] = parseInt(cur).toString(2).padStart(4, '0')
        return acc
    },
    {} as InsToCodeType
) as InsToCodeType

/**
 * 获取指令
 */
export function getInstruction(ir: number) {
    const irHigh4Bit = ir >> 4
    let ins = instructions[irHigh4Bit as keyof typeof instructions]
    let isIns = true
    if (!ins) {
        isIns = false
        ins = instructions.__EMPTY__
    }
    return {
        ins,
        isIns,
        fullIns: `${ins} ${ir.toString(2).padStart(8, '0').substring(4)}`,
    }
}

/**
 * 获取指令对应的二进制码
 */
export function getInsCode(ins: string) {
    const [insName, suf = ''] = ins.split(' ')
    const insCode = insToCode[insName as any]
    if (!insCode) {
        return ins
    }
    return '0b' + insCode + suf.padEnd(4, '0')
}

/**
 * decoder模块
 */
export const decoder: IDecoder = {
    name: 'decoder',
    dataItem: null,
    state: {
        ins: instructions.__EMPTY__,
    },
    async setState(prop, value) {
        this.state[prop] = value
        return
    },
    async change() {
        // 当sm_value(decoder_en)或者ir的值发生变化时
        const decoder_en = sm.state.sm_value
        const irValue = ir.state.value
        if (!decoder_en) {
            this.setState('ins', instructions.__EMPTY__)
        } else {
            // 解析ir的值，得到指令
            const { ins } = getInstruction(irValue)
            this.setState('ins', ins)
        }

        // 开始动画
        // decoder的中心点
        const decoderCenterPoint = getCenterPoint(sizeConfig.decoder)
        // decoder的底部中心点
        const decoderBottomCenterPoint = getCenterPoint(
            sizeConfig.decoder,
            'bottom'
        )
        // conSignal的底部中心点
        const conSignalBottomCenterPoint = getCenterPoint(
            sizeConfig.conSignal,
            'bottom'
        )
        this.dataItem?.setText(toBinaryStr(irValue))
        this.dataItem?.setPosition(decoderBottomCenterPoint)
        anime.remove(this.dataItem?.dom!)
        // 动画
        const timeline = anime
            .timeline({
                easing: 'linear',
                duration: (edgeConfig.time.neg + edgeConfig.time.low) * 0.05,
                targets: this.dataItem?.dom,
                complete: () => {
                    // 通知conSignal模块
                    conSignal.change()
                },
            })
            .add({
                x: decoderCenterPoint.x,
                y: decoderCenterPoint.y - 10,
            })
            .add({
                backgroundColor: '#fffbe6',
                complete: () => {
                    this.dataItem?.setText(this.state.ins)
                },
            })
            .add({
                backgroundColor: '#fff',
            })
            .add({
                x: conSignalBottomCenterPoint.x,
                y: conSignalBottomCenterPoint.y,
                backgroundColor: 'rgba(255, 255, 255, 0.5)',
            })
        return timeline.finished
    },
    init() {
        // conSignal的底部中心点
        const conSignalBottomCenterPoint = getCenterPoint(
            sizeConfig.conSignal,
            'bottom'
        )
        this.dataItem = createDataValue(
            this.state.ins,
            this.state.ins,
            conSignalBottomCenterPoint
        )
        // delete this.dataItem.dom.dataset.value
    },
}
